<?php

namespace App\Game\Core;
/**
 * 麻将基本胡牌 （不包含小七对）
 *
 * 此类检测是否胡牌，返回胡牌的组合, 你可以再将获得的胡牌组合，进行相应牌型检测获得牌型
 * 不能用于小七对的胡牌检测
 *
 */
class MJ
{

    // 万 筒 条 东南西北 中发白 花
    private $cardTypes = [
        'wan' => [1, 2, 3, 4, 5, 6, 7, 8, 9],
        'tong' => [11, 12, 13, 14, 15, 16, 17, 18, 19],
        'tiao' => [21, 22, 23, 24, 25, 26, 27, 28, 29],
        'dnxb' => [31, 32, 33, 34],
        'zfb' => [35, 36, 37],
        'flower' => []
    ];


    private $stack = [];    // 牌组栈
    private $cards = [];    // 剩余手牌
    private $pairs = 0;    // 将对个数
    private $raw = false;    // 是否直接返牌组栈
    private $number = [];    // 牌堆

    private $is4d = false;    // 包含4对子特殊牌型

    //types麻将牌种类:['wan','tong','tiao','dnxb','zfb','flower']
    public function __construct(array $types = [], bool $raw = false)
    {
        if (empty($types)) {
            $number = [
                1, 2, 3, 4, 5, 6, 7, 8, 9,
                11, 12, 13, 14, 15, 16, 17, 18, 19,
                21, 22, 23, 24, 25, 26, 27, 28, 29
            ];
        } else {
            foreach ($types as $v) {
                if (isset($this->cardTypes[$v])) {
                    $number = array_merge($this->cardTypes[$v]);
                }
            }
        }
        $this->number = $number;
        $this->raw = $raw;
    }


    /*
    * 发牌
    */
    public function dealCards($users = array())
    {
        $cards = array_merge($this->number, $this->number, $this->number, $this->number);
        //洗牌
        $user_card = $hand = array();
        shuffle($cards);
        //每人发13张牌
        for ($i = 0; $i < 4; $i++) {
            $cardArr = array_splice($cards, 0, 13);
            sort($cardArr);
            $user_card[] = $cardArr;
        }
        $hand = $cards;
        if (!empty($users)) {
            $card['hand'] = $hand;
            foreach ($users as $k => $v) {
                $card[$v] = $user_card[$k];
                $show_card[$v] = $this->crateCard($user_card[$k]);
            }
        } else {
            $card = array('user1' => $user_card[0], 'user2' => $user_card[1], 'user3' =>
                $user_card[2], 'user4' => $user_card[3], 'hand' => $hand);
            $show_card = array('user1' => $this->crateCard($user_card[0]), 'user2' =>
                $this->crateCard($user_card[1]), 'user3' => $this->crateCard($user_card[2]), 'user4' => $this->crateCard($user_card[3]), 'hand'
            => $this->crateCard($hand));
        }
        return array('card' => $card, 'show_card' => $show_card);
    }


    public function crateCard(array $userCards)
    {
        return $userCards;
    }


    public function clear(bool $raw = false)
    {
        $this->stack = [];
        $this->cards = [];
        $this->pairs = 0;
        $this->raw = $raw;
    }

    public function start(array $cards, array $peng = [],
                          array $gang = [])
    {
        $this->cards = $cards;
        sort($this->cards);


//        dd('sort_cards', $this->cards);

        //牌数小于14或者大于18，则必定不能胡牌
        $num = count($cards);
        if (($num < 14) || ($num > 18)) {
            return [];
        }
        // 特殊牌数：1，1，3，3，3，3
        $countValuesArr = array_count_values($cards);
        dd('countValuesArr', $countValuesArr);
//        ddNode(1);
        // 杠牌
        if (($num - 14) > 0) {
//            ddNode(2);
            $this->try_quadra($countValuesArr);
        } else {
//            ddNode(3);
            //七对
            if (count($countValuesArr) == 7) {
//                ddNode(4);
                return array_chunk($this->cards, 2);
            }
        }
        // 特殊牌数组合：[1,1,3,3,3,3]
        $diff = array_diff($countValuesArr, [1, 1, 3, 3, 3, 3]);
        if (empty($diff)) {
            $this->cards = $cards;
            sort($this->cards);
            if ($this->try_hu2()) {
                if ($this->raw) {
                    // 直接返回牌组栈
                    return $this->stack;
                } else {
                    // 返回胡牌的排列
                    $win_cards = [];
                    foreach ($this->stack as $e) {
                        $win_cards[] = $e;
                    }
                    return $win_cards;
                }
            }
        } else {
            $this->check4D($countValuesArr);
            if ($this->try_hu()) {
//            ddNode(6);
                if ($this->raw) {
                    // 直接返回牌组栈
                    return $this->stack;
                } else {
                    // 返回胡牌的排列
                    $win_cards = [];
                    foreach ($this->stack as $e) {
                        $win_cards[] = $e;
                    }
                    return $win_cards;
                }
            }
        }
        return [];
    }

    // 将牌组压栈
    private function push(array $group)
    {
        array_push($this->stack, $group);
    }

    // 牌组出栈
    private function roll_back()
    {
        $group = array_pop($this->stack);
        array_push($this->cards, $group);
        sort($this->cards);
    }

    // 杠，则将此组牌压栈
    private function try_quadra(array $countValuesArr)
    {
//        dd('rs', $rs);
        foreach ($countValuesArr as $k => $v) {
            if ($v == 4) {
                $this->cards = $this->array_remove($this->cards, $k, 4);
//                dd('this->cards', $this->cards);
                $this->push([$k, $k, $k, $k]);
//                dd('this->stack', $this->stack);
            }
        }
    }


    // 刻子，则将此组牌压栈
    private function try_triplet(int $card)
    {
        if ($this->array_count($this->cards, $card) >= 3) {
            $this->cards = $this->array_remove($this->cards, $card, 3);
            $this->push([$card, $card, $card]);
            return true;
        } else
            return false;
    }

    // 顺子，则将此组牌压栈
    private function try_along(int $card)
    {
        if (!in_array($card, $this->number)) {
            return false;
        }
        if (in_array($card, $this->cards) && in_array($card + 1, $this->cards) && in_array($card + 2, $this->cards)) {
            $this->cards = $this->array_remove($this->cards, $card);
            $this->cards = $this->array_remove($this->cards, $card + 1);
            $this->cards = $this->array_remove($this->cards, $card + 2);
            $this->push([$card, $card + 1, $card + 2]);
            return true;
        } else
            return false;
    }

    // 对子，对子只能有一个
    private function try_sub(int $card)
    {
        if ($this->pairs == 1)
            return false;

        if ($this->array_count($this->cards, $card) >= 2) {
            $this->cards = $this->array_remove($this->cards, $card, 2);
            $this->push([$card, $card]);
            $this->pairs = 1;
            return true;
        } else
            return false;
    }


    // 回溯尝试组成顺子刻子对子
    private function try_hu()
    {
        if (!$this->cards) {
            if ($this->pairs == 1)
                return true;
            else
                return false;
        }

//        dd('cards', $this->cards);
        $active_card = reset($this->cards);
        //找刻子
        if ($this->try_triplet($active_card)) {
//            ddNode('k');
            if (!$this->try_hu())
                $this->roll_back();
            else
                return true;
        }

        if ($this->is4d) {
            //找顺子
            if ($this->try_along($active_card)) {
//                ddNode('s');
                if (!$this->try_hu())
                    $this->roll_back();
                else
                    return true;
            }
            //找对子
            if ($this->try_sub($active_card)) {
//                ddNode('d');
//                ddNode("< $active_card >");
                if (!$this->try_hu()) {
                    $this->roll_back();
                    $this->pairs = 0;
                } else
                    return true;
            }
        } else {
            //找对子
            if ($this->try_sub($active_card)) {
//                ddNode('d');
//                ddNode("< $active_card >");
                if (!$this->try_hu()) {
                    $this->roll_back();
                    $this->pairs = 0;
                } else
                    return true;
            }
            //找顺子
            if ($this->try_along($active_card)) {
//                ddNode('s');
                if (!$this->try_hu())
                    $this->roll_back();
                else
                    return true;
            }
        }


        return false;
    }

    // 回溯尝试组成顺子刻子对子
    private function try_hu2()
    {
        if (!$this->cards) {
            if ($this->pairs == 1)
                return true;
            else
                return false;
        }

        $active_card = reset($this->cards);

        //找顺子
        if ($this->try_along($active_card)) {
            if (!$this->try_hu2())
                $this->roll_back();
            else
                return true;
        }
        //找刻子
        if ($this->try_triplet($active_card)) {
            if (!$this->try_hu2())
                $this->roll_back();
            else
                return true;
        }
        //找对子
        if ($this->try_sub($active_card)) {
            if (!$this->try_hu2()) {
                $this->roll_back();
                $this->pairs = 0;
            } else
                return true;
        }


        return false;
    }

    private function array_remove(array $cards, int $card, int $d = 1)
    {
        foreach ($cards as $k => $v) {
            if ($v == $card && $d) {
                $d--;
                unset($cards[$k]);
            }
        }
        return $cards;
    }

    private function array_count(array $cards, int $card)
    {
        @$rs = array_count_values($cards);
        foreach ($rs as $k => $v) {
            if ($card == $k) return $v;
        }
        return 0;
    }

    private function is_qidui(array $countValuesArr)
    {
        foreach ($countValuesArr as $k => $v) {
            if ($v % 2 != 0) return false;
        }
        return true;
    }

    // 校验是否包含4对子，
    private function check4D(array $countValuesArr)
    {
        $num = 0;
        foreach ($countValuesArr as $k => $v) {
            if ($v == 2) {
                $num += 1;
            }
        }
        $this->is4d = $num == 4 ? true : false;
    }


}